package edu.ustb.efws.meter.controller;

import edu.ustb.efws.EfwsProperties;
import edu.ustb.efws.base.domain.Position;
import edu.ustb.efws.base.service.PositionService;
import edu.ustb.efws.controller.vo.ResultVo;
import edu.ustb.efws.meter.controller.form.*;
import edu.ustb.efws.meter.controller.vo.ExportDocVo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/report")
@Api(value = "/report", tags = "导出WORD报表")
public class ReportController {
    private static final Logger LOGGER= LoggerFactory.getLogger(ReportController.class);

    private final PositionService positionService;
    private final Configuration configuration;
    private final EfwsProperties properties;

    @Autowired
    public ReportController(PositionService positionService, Configuration configuration,
                            EfwsProperties properties) {
        this.positionService = positionService;
        this.configuration = configuration;
        this.properties = properties;
    }

    @PostMapping(value = "year", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出年报表")
    public ResultVo<ExportDocVo> exportYear(@Valid @RequestBody YearRepForm form){
        Position position = positionService.get(form.getPositionId());
        String equipment = position.getRiskArea() + position.getRiskPosition();

        Map<String, String> params = new HashMap<>();
        params.put("equipment", equipment);
        params.put("year", form.getYear().toString());
        params.put("count", "12");
        params.put("image", imageBase64(form.getChart()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        byte[] bytes = docProcess(params, "year.ftl");
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }

        String title = String.format("安全生产风险评估%s设备%d年分析报告.doc", equipment, form.getYear());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    private String imageBase64(String v){
        return v.replace("\n", "\r\n").replace("\r\r\n", "\r\n");
    }

    private byte[] docProcess(Map<String, String> params, String ftl){
        try(ByteArrayOutputStream os = new ByteArrayOutputStream();
            Writer writer = new OutputStreamWriter(os)){

            Template template = configuration.getTemplate(ftl);
            template.process(params, writer);
            return os.toByteArray();
        }catch (IOException | TemplateException e){
            LOGGER.error("Free mark process fail ftl={}, error={}", ftl, e.getMessage());
            return new byte[0];
        }
    }

    private boolean writeTempFile(String docId, byte[] bytes){
        String path = properties.getTmpPath() + "/" + docId;
        try(OutputStream os = new FileOutputStream(path)){
            os.write(bytes);
            os.flush();
            return false;
        }catch (IOException e){
            LOGGER.error("Writ file fail path={}, error={}", path, e.getMessage());
            return true;
        }
    }

    @PostMapping(value = "month", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出月报表")
    public ResultVo<ExportDocVo> exportMonth(@Valid @RequestBody MonthRepForm form){
        Position position = positionService.get(form.getPositionId());
        String equipment = position.getRiskArea() + position.getRiskPosition();


        Map<String, String> params = new HashMap<>();
        params.put("equipment", equipment);
        params.put("year", form.getYear().toString());
        params.put("month", form.getMonth().toString());
        params.put("count", "30");
        params.put("image", imageBase64(form.getChart()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        byte[] bytes = docProcess(params, "month.ftl");
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }

        String title = String.format("安全生产风险评估%s设备%d年%d月分析报告.doc", equipment, form.getYear(), form.getMonth());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    @PostMapping(value = "day", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出天报表")
    public ResultVo<ExportDocVo> exportDay(@Valid @RequestBody DayRepForm form){
        Position position = positionService.get(form.getPositionId());
        String equipment = position.getRiskArea() + position.getRiskPosition();

        Map<String, String> params = new HashMap<>();
        params.put("equipment", equipment);
        params.put("year", form.getYear().toString());
        params.put("month", form.getMonth().toString());
        params.put("day", form.getDay().toString());
        params.put("image", imageBase64(form.getChart()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        byte[] bytes = docProcess(params, "day.ftl");
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }


        String title = String.format("安全生产风险评估%s设备%d年%d月%d日风险趋势图.doc",
                equipment, form.getYear(), form.getMonth(), form.getDay());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    @PostMapping(value = "thosYear", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出年报表")
    public ResultVo<ExportDocVo> exportThosYear(@Valid @RequestBody ThosYearRepForm form){
        Map<String, String> params = new HashMap<>();
        params.put("imageO", imageBase64(form.getChartT()));
        params.put("imageH", imageBase64(form.getChartH()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        String template = form.getPositionId() < 53? "zfj_year.ftl": "zgj_year.ftl";
        byte[] bytes = docProcess(params, template);
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }

        String name = form.getPositionId() < 53? "分拣区": "高架库";
        String title = String.format("%s温湿度%d年度报告.doc", name, form.getYear());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    @PostMapping(value = "thosMonth", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出月报表")
    public ResultVo<ExportDocVo> exportThosMonth(@Valid @RequestBody ThosMonthRepForm form){
        Map<String, String> params = new HashMap<>();
        params.put("imageO", imageBase64(form.getChartT()));
        params.put("imageH", imageBase64(form.getChartH()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        String template = form.getPositionId() < 53? "zfj_month.ftl": "zgj_month.ftl";
        byte[] bytes = docProcess(params, template);
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }

        String name = form.getPositionId() < 53? "分拣区": "高架库";
        String title = String.format("%s温湿度%d年%d月度报告.doc", name, form.getYear(), form.getMonth());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    @PostMapping(value = "thosDay", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("导出年报表")
    public ResultVo<ExportDocVo> exportThosDay(@Valid @RequestBody ThosDayRepForm form){
        Map<String, String> params = new HashMap<>();
        params.put("imageO", imageBase64(form.getChartT()));
        params.put("imageH", imageBase64(form.getChartH()));

        String docId = StringUtils.remove(UUID.randomUUID().toString(), "-");
        String template = form.getPositionId() < 53? "zfj_day.ftl": "zgj_day.ftl";
        byte[] bytes = docProcess(params, template);
        if(writeTempFile(docId, bytes)){
            return ResultVo.error(new String[]{"生成报表失败"});
        }

        String name = form.getPositionId() < 53? "分拣区": "高架库";
        String title = String.format("%s温湿度%d年%d月%d日度报告.doc", name, form.getYear(), form.getMonth(), form.getDay());
        return ResultVo.success(new ExportDocVo(docId, title));
    }

    @GetMapping(value = "downland")
    @ApiOperation("下载报表")
    public void downland(HttpServletResponse response,
                         @RequestParam @ApiParam(value = "docId", required = true) String docId,
                         @RequestParam @ApiParam(value = "标题", required = true) String title){

        String path = properties.getTmpPath() + "/" + docId;
        File file = new File(path);
        boolean success = false;
        if(file.exists()){
           try(InputStream in = new FileInputStream(file);
               OutputStream os = response.getOutputStream()){
               setContentDisposition(response, title);
               byte[] bytes = new byte[1024];
               int len;
               while((len = in.read(bytes)) != -1){
                   os.write(bytes, 0, len);
               }
               os.flush();
               success = true;
           }catch (IOException e){
               LOGGER.error("Read doc file fail path={}, error={}", path, e.getMessage());
           }
        }

        if(!success){
            response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_VALUE);
            try(OutputStream os = response.getOutputStream()){
                os.write("导出报表错误".getBytes(StandardCharsets.UTF_8));
                os.flush();
            }catch (IOException e){
                //None instance
            }
        }

        if(file.exists()){
            boolean ok = file.delete();
            LOGGER.debug("Delete doc file path={}, ok={}", path, ok);
        }
    }

    /**
     * 设置Http header Content-Disposition
     *
     * @param response {@link HttpServletResponse}
     * @param filename 文件名
     */
    public static void setContentDisposition(HttpServletResponse response, String filename){
        try{
            final String charset = "UTF-8";
            String name =  charset + "''"+ URLEncoder.encode(filename, charset);
            response.setHeader("Content-Disposition", "attachment; filename*=" + name);
        }catch (UnsupportedEncodingException e){
            //none instance
        }
    }
}
